/**
 * Author: 	Gareth Arch
 * CreateDate:	08/21/2009
 * Description: This component creates a "deselectable value" that maps to the prompt value.  If
 * 				the user selects a value from the combobox, the "prompt" property is created as an item in the
 * 				dataprovider for the combobox (a new arraycollection is created so the original arraycollection is maintained).
 * 				If the "deselectable value" is selected, the combobox is reset to the prompt, and removed from the dataprovider.
*/
package com.flexoop.lib.components.form {

	import flash.events.Event;
	
	import mx.collections.ArrayCollection;
	import mx.controls.ComboBox;
	import mx.controls.List;
	import mx.core.ClassFactory;
	import mx.utils.ObjectUtil;
	
	[Event(type="flash.events.Event", name="selectedIndexReset")]
	[Event(type="flash.events.Event", name="deselectableLabelAdded")]
	[Event(type="flash.events.Event", name="deselectableLabelRemoved")]

	public class DeselectableComboBox extends ComboBox {
		
		public static const SELECTED_INDEX_RESET:String			= "selectedIndexReset";
		public static const DESELECTABLE_LABEL_ADDED:String		= "deselectableLabelAdded";
		public static const DESELECTABLE_LABEL_REMOVED:String	= "deselectableLabelRemoved";
	
		private var _deselectableLabelValue:Object = {};
		
		// This will determine whether to create a copy of the dataProvider
		// 		or just use the one passed in.
		// This should be set to "true" if the dataProvider is going to be used by multiple controls
		// 		otherwise the "prompt" value that is added will show in the other controls
		private var _makeDataProviderCopy:Boolean = false;
		
		public function get makeDataProviderCopy():Boolean {
			return _makeDataProviderCopy;
		}
		
		public function set makeDataProviderCopy( value:Boolean ):void {
			this._makeDataProviderCopy = value;
		}
		
		// This will add a value to the dataprovider that allows the user to click
		// the value to reset the combobox to selectedIndex = -1
		private var _useDeselectableLabel:Boolean = true;
		
		public function get useDeselectableLabel():Boolean {
			return _useDeselectableLabel;
		}
		
		public function set useDeselectableLabel( value:Boolean ):void {
			this._useDeselectableLabel = value;
			// have to reset class factory if using deselectable label
			this.dropdownFactory = value ? new ClassFactory( List ) : new ClassFactory( DeselectableList );
		}
		
		public function DeselectableComboBox( useDeselectableLabel:Boolean=true, makeDataProviderCopy:Boolean=false ) {
			super();
			this.dropdownFactory = useDeselectableLabel ? new ClassFactory( List ) : new ClassFactory( DeselectableList );
			this.useDeselectableLabel = useDeselectableLabel;
			this.makeDataProviderCopy = makeDataProviderCopy;
		}
		
		override public function set labelField( value:String ):void {
			super.labelField = value;
			_deselectableLabelValue[ value ] = ( this.prompt && this.prompt.length > 0 ) ? this.prompt : '' ;
		}
		
		override public function set prompt( value:String ):void {
			super.prompt = value;
			if ( this.labelField.length > 0 ) {
				_deselectableLabelValue[ this.labelField ] = value;
			}
		}
		
		override public function set dataProvider( value:Object ):void {
			// if using deselectable value and there is a value, and value does not already contain deselectable value, add it
			if ( value is Array ) {
				super.dataProvider = new ArrayCollection( ( makeDataProviderCopy ) ? ObjectUtil.copy( value ) as Array : value as Array );
			} else {
				super.dataProvider = makeDataProviderCopy ? ObjectUtil.copy( value ) as ArrayCollection : value as ArrayCollection;
			}
		}
		
		override public function set selectedIndex( value:int ):void {
			super.selectedIndex = value;
			// if the control is using the deselectable label
			if ( useDeselectableLabel ) {
				// and the user has selected the deselectable value
				if ( this.selectedItem == _deselectableLabelValue ) {
					// reset the selectedIndex to the prompt, which causes this function to be called again, which will now remove the deselectable label
					this.selectedIndex = -1;
					this.dispatchEvent( new Event( DeselectableComboBox.SELECTED_INDEX_RESET ) );
				} else if ( this.selectedIndex == -1 ) {
					// and remove the value from the dataprovider
					var _deselectableValueIndex:Number = deselectableValueIndex( this.dataProvider );

					if ( _deselectableValueIndex > - 1 ) {
						if ( this.dataProvider is Array ) {
							( this.dataProvider as Array ).splice( _deselectableValueIndex, 1 );
						} else {
							( this.dataProvider as ArrayCollection ).removeItemAt( _deselectableValueIndex );
						}
					}
					this.dispatchEvent( new Event( DeselectableComboBox.DESELECTABLE_LABEL_REMOVED ) );
				} else {
					// if the dataprovider does not already contain the deselectable label, add it
					if ( deselectableValueIndex( this.dataProvider ) == -1 ) {
						if ( this.dataProvider is Array ) {
							( this.dataProvider as Array ).unshift( _deselectableLabelValue );
						} else {
							( this.dataProvider as ArrayCollection ).addItemAt( _deselectableLabelValue, 0 );
						}
					}
					this.dispatchEvent( new Event( DeselectableComboBox.DESELECTABLE_LABEL_ADDED ) );
				}
			}
		}
		
		/**
		 * @private
		 * 
		 * @return	index of the deselectable label value if it is found, else -1
		 */
		private function deselectableValueIndex( value:Object ):Number {
			var _returnValue:Number = -1;
			
			if ( value is Array ) {
				var _i:int = 0;
				var _length:int = ( this.dataProvider as Array ).length;
				while ( _i < _length ) {
					if ( ( value as Array )[ _i ] == _deselectableLabelValue ) {
						_returnValue = _i;
						break;
					}
					_i++;
				}
			} else if ( value is ArrayCollection ) {
				_returnValue = ( value as ArrayCollection ).getItemIndex( _deselectableLabelValue );
			}
			
			return _returnValue;
		}

	}
}